#include "D_List.h"
#include <stdlib.h>

inline int IsEmpty(D_List *list)
{
    return list == NULL || list->len == 0;
}

extern D_List *InitList()
{
    D_List *newList = (D_List *)malloc(sizeof(D_List));
    newList->len = 0;
    newList->head = NULL;
}

extern void Insert_Front(D_List* list, ElementType data)
{
    D_ListNode *newNode = (D_ListNode *)malloc(sizeof(D_ListNode));
    newNode->data = data;
    newNode->prev = newNode->next = newNode;
    if(list->head != NULL)
    {
        newNode->next = list->head;
        newNode->prev = list->head->prev;
        list->head->prev->next = newNode;
        list->head->prev = newNode;
    }
    list->head = newNode;
    ++list->len;
}

extern void Insert_Back(D_List *list, ElementType data)
{
    D_ListNode *newNode = (D_ListNode *)malloc(sizeof(D_ListNode));
    newNode->data = data;
    newNode->prev = newNode->next = newNode;
    if(list->head == NULL)
    {
        list->head = newNode;
    }
    else
    {
        newNode->next = list->head;
        newNode->prev = list->head->prev;
        list->head->prev->next = newNode;
        list->head->prev = newNode;
    }
    ++list->len;
}

extern void Insert_Before(D_List *list, ElementType findData, ElementType data)
{
    if(IsEmpty(list))
    {
        return;
    }  
    D_ListNode **tpNode = &list->head;
    while (*tpNode != NULL)
    {
        if((*tpNode)->data == findData)
        {
            D_ListNode *newNode = (D_ListNode *)malloc(sizeof(D_ListNode));
            newNode->data = data;
            newNode->next = *tpNode;
            newNode->prev = (*tpNode)->prev;
            (*tpNode)->prev->next = newNode;
            (*tpNode)->prev = newNode;
            ++list->len;
            if(*tpNode == list->head)
            {
                list->head = newNode;
            }
            return;
        }
        tpNode = &(*tpNode)->next;
    }
}

extern void Insert_After(D_List *list, ElementType findData, ElementType data)
{
    if(IsEmpty(list))
    {
        return;
    }  
    D_ListNode **tpNode = &list->head;
    while (*tpNode != NULL)
    {
        if((*tpNode)->data == findData)
        {
            D_ListNode *newNode = (D_ListNode *)malloc(sizeof(D_ListNode));
            newNode->data = data;
            newNode->prev = *tpNode;
            newNode->next = (*tpNode)->next;
            (*tpNode)->next->prev = newNode;
            (*tpNode)->next = newNode;
            ++list->len;
            return;
        }
        tpNode = &(*tpNode)->next;
    }
}

extern void Remove_Front(D_List *list)
{
    if(IsEmpty(list))
    {
        return;
    }
    D_ListNode **tpNode = &(list->head);
    D_ListNode *delNode = *tpNode;
    (*tpNode)->prev->next = (*tpNode)->next;
    (*tpNode)->next->prev = (*tpNode)->prev;
    *tpNode = (*tpNode)->next;
    free(delNode);
    --list->len;
}

extern void Remove_Back(D_List *list)
{
    if(IsEmpty(list))
    {
        return;
    }
    D_ListNode **tpNode = &(list->head->prev);
    D_ListNode *delNode = *tpNode;
    (*tpNode)->prev->next = (*tpNode)->next;
    (*tpNode)->next->prev = (*tpNode)->prev;
    *tpNode = (*tpNode)->next;
    free(delNode);
    --list->len;
}

extern void Remove(D_List *list, ElementType findData)
{
    if(IsEmpty(list))
    {
        return;
    }
    D_ListNode **tpNode = &list->head;
    while (*tpNode != NULL)
    {
        if((*tpNode)->data == findData)
        {
            D_ListNode *delNode = *tpNode;
            (*tpNode)->prev->next = (*tpNode)->next;
            (*tpNode)->next->prev = (*tpNode)->prev;
            *tpNode = (*tpNode)->next;
            free(delNode);
            --list->len;
            return;
        }
        tpNode = &(*tpNode)->next;
    }  
}

extern D_ListNode *GetNodeByElement(D_List *list, ElementType findData)
{
    if(IsEmpty(list))
    {
        return NULL;
    }
    D_ListNode *tpNode = list->head;
    for (int i = 0; i < list->len; ++i)
    {
        if(tpNode->data == findData)
        {
            return tpNode;
        }
        tpNode = tpNode->next;
    }
    return NULL;
}

extern D_ListNode *GetNodeByIndex(D_List *list, int index)
{
    if(IsEmpty(list) || index < 0 || list->len < index)
    {
        return NULL;
    }
    int i;
    D_ListNode *tpNode = list->head;
    if(index < list->len / 2)
    {
        for (i = 0; i < index; ++i)
        {
            tpNode = tpNode->next;
        }
    }
    else
    {
        for (i = list->len; i > index; --i)
        {
            tpNode = tpNode->prev;
        }
    }
    return tpNode;
}

extern int SetDataByIndex(D_List *list, int index, ElementType newData)
{
    D_ListNode *findNode = GetNodeByIndex(list, index);
    if(findNode != NULL)
    {
        findNode->data = newData;
        return 1;
    }
    return 0;
}

extern int SetDataByElement(D_List *list, ElementType findData, ElementType newData)
{
    D_ListNode *findNode = GetNodeByElement(list, findData);
    if(findNode != NULL)
    {
        findNode->data = newData;
        return 1;
    }
    return 0;  
}